﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PerCederberg.Grammatica.Runtime;
using SafeScript.MetaModel.Statements;

namespace SafeScript.MetaModel
{
    internal class UnparsedClassMember
    {
        public Node Node;
        public Using Use;
        public bool Static;
        public Node Attributes;
        public Node Comments;
    }

    public interface INamed
    {
        string Name { get; set; }
    }

    public class Namespace : Scope, ITypeDef, INamed
    {
        /// <summary>
        /// Used to avoid that instance member functions/properties/variables are called from static member functions.
        /// </summary>
        private bool parsingStaticCode;
        /// <summary>
        /// Used to identify the type of "value" when used inside a property getter.
        /// </summary>
        private ITypeDef parsingPropertySetType;
        /// <summary>
        /// The MemberFunction, FunctionOperator, Getter, Setter or Constructor to which the statements
        /// belong that are currently being parsed.
        /// </summary>
        private object parsingContext;
        /// <summary>
        /// Indicates that a return statement has been parsed in the current parsing context.
        /// </summary>
        private bool parsingContextHasReturns;
        /// <summary>
        /// Indicates that the expression being parsed is in fact a binding
        /// </summary>
        private bool parsingExpressionIsBinding;

        public string Name { get; set; }
        /// <summary>
        /// May be null.
        /// </summary>
        public StatementList Statements;

        /// <summary>
        /// May be null.
        /// </summary>
        public MemberAttribute[] Attributes { get; set; }

        public override string ToString()
        {
            return this.Name;
        }

        /// <summary>
        /// Holds a list of members which need to be parsed later when all namespaces are known
        /// and the using directives have been resolved
        /// </summary>
        private List<UnparsedClassMember> classMembers = new List<UnparsedClassMember>();
        /// <summary>
        /// All sets of using directives used inside this namespace
        /// </summary>
        private List<Using> usingClauses = new List<Using>();
        private List<Node> unparsedStatements = new List<Node>();
        private Using unparsedStatementsUsing;

        #region ITypeDef Members
        
        public virtual bool IsEqualTo(ITypeDef typedef)
        {
            return this == typedef as Namespace;
        }

        public virtual bool CanBeAssignedTo(ITypeDef typedef)
        {
            return this.IsEqualTo(typedef);
        }

        public virtual int GetInheritanceDepth(ITypeDef typedef)
        {
            return this.IsEqualTo(typedef) ? 0 : -1;
        }

        public virtual ITypeDef Instantiate(Dictionary<TemplateParameterType,ITypeDef> templateArguments)
        {
            System.Diagnostics.Debug.Assert(false, "Programming error");
            return null;
        }

        #endregion

        /// <summary>
        /// Parses the body of a namespace/class/interface
        /// </summary>
        /// <param name="main"></param>
        /// <param name="report"></param>
        /// <param name="parentUsing"></param>
        public void ParseMain(Node main, ErrorReport report, Using parentUsing)
        {
            // Parse the using clauses
            Using use = new Using() { ParentUsing = parentUsing };
            while (main != null && main.Id == (int)SafeScriptConstants.MAIN_WITH_USING)
            {
                Node usingClause = Helper.GetNode(main, SafeScriptConstants.USING_CLAUSE);
                if (usingClause != null)
                {
                    ScopedIdentifier ident = new ScopedIdentifier(Helper.GetNode(usingClause, SafeScriptConstants.SCOPED_IDENTIFIER));
                    if (usingClause.Count == 3)
                        use.ImportedTmp.Add(new ScopeResolver() { Context = this, Ident = ident, Location = usingClause });
                    else
                    {
                        System.Diagnostics.Debug.Assert(usingClause.Count == 5);
                        string alias = (usingClause[3] as Token).Image;
                        use.AliasTmp[alias] = new ScopeResolver() { Context = this, Ident = ident, Location = usingClause };
                    }
                    main = Helper.GetNode(main, SafeScriptConstants.MAIN_WITH_USING);
                }
                else if (Helper.GetToken(main, SafeScriptConstants.IMPORT) != null)
                {
                    string str = Helper.GetToken(main, SafeScriptConstants.STRING).Image;
                    // TODO: proper escape the string
                    str = str.Substring(1, str.Length - 2);
                    // Convert to windows file names
                    str = str.Replace('/', '\\');
                    Compiler.CurrentCompiler.AddFile(str);

                    main = Helper.GetNode(main, SafeScriptConstants.MAIN_WITH_USING);
                }
                else
                {
                    main = Helper.GetNode(main, SafeScriptConstants.MAIN);
                    break;
                }
            }
            this.usingClauses.Add(use);

            if (this is AbstractType && (this as AbstractType).TemplateParameters.Count > 0)
            {
                Dictionary<string, object> tps = new Dictionary<string, object>();
                foreach (TemplateParameterType tpt in (this as AbstractType).TemplateParameters)
                {
                    tps[tpt.Name] = tpt;
                }
                // Add the template parameter types to the using
                use.Stack.Push(tps);
            }

            // Parse the members of the namespace/class/interface
            while (main != null)
            {
                Node memberDef = Helper.GetNode(main, SafeScriptConstants.MEMBER);
                // A new member?
                if (memberDef != null)
                {
                    // A new namespace?
                    Node namespaceDef = Helper.GetNode(memberDef, SafeScriptConstants.NAMESPACE_DEF);
                    if (namespaceDef != null)
                    {
                        if (this is AbstractType)
                        {
                            report.Add(new ErrorEntry(namespaceDef) { Info = this.Name, Type = ErrorType.NamespaceInType });
                        }
                        else
                        {
                            Namespace nspace = this.CreateNestedNamespaces(namespaceDef, report, use);

                            Node attributes = Helper.GetNode(main, SafeScriptConstants.ATTRIBUTES);
                            if (attributes != null)
                                nspace.Attributes = this.ParseAttributes(attributes, nspace, report);
                        }
                    }
                    // A new class?
                    else
                    {
                        Node classDef = Helper.GetNode(memberDef, SafeScriptConstants.CLASS_DEF);
                        if (classDef != null)
                        {
                            Class klasse = new Class();
                            klasse.ParentScope = this;
                            klasse.ParseDef(classDef, report, use);
                            if (Helper.GetToken(classDef, SafeScriptConstants.ABSTRACT) != null)
                                klasse.IsAbstract = true;
                            if (Helper.GetToken(classDef, SafeScriptConstants.SEALED) != null)
                                klasse.IsSealed = true;

                            Node attributes = Helper.GetNode(main, SafeScriptConstants.ATTRIBUTES);
                            if (attributes != null)
                                klasse.Attributes = this.ParseAttributes(attributes, klasse, report);

                            // If this is a class nested in a template class, we hand down these parameters
                            if (this is AbstractType)
                            {
                                AbstractType athis = this as AbstractType;
                                if (athis.TemplateParameters.Count > 0)
                                {
                                    foreach (var t in athis.TemplateParameters)
                                        klasse.TemplateParameters.Add(t);
                                }
                            }

                            if (this is Interface)
                                report.Add(new ErrorEntry(classDef) { Type = ErrorType.ClassInInterface, Info = klasse.Name });
                            else
                                this.ScopeElements[klasse.Name] = klasse;
                        }
                        else
                        {
                            Node ifaceDef = Helper.GetNode(memberDef, SafeScriptConstants.INTERFACE_DEF);
                            if (ifaceDef != null)
                            {
                                Interface iface = new Interface();
                                iface.ParentScope = this;
                                iface.ParseDef(ifaceDef, report, use);

                                Node attributes = Helper.GetNode(main, SafeScriptConstants.ATTRIBUTES);
                                if (attributes != null)
                                    iface.Attributes = this.ParseAttributes(attributes, iface, report);

                                if (this is Interface)
                                    report.Add(new ErrorEntry(ifaceDef) { Type = ErrorType.InterfaceInInterface, Info = iface.Name });
                                else
                                    this.ScopeElements[iface.Name] = iface;
                            }
                            else
                            {
                                Node enumDef = Helper.GetNode(memberDef, SafeScriptConstants.ENUM_DEF);
                                if (enumDef != null)
                                {
                                    EnumTypeDef e = new EnumTypeDef();
                                    e.ParseDef(enumDef, report, use);

                                    Node attributes = Helper.GetNode(main, SafeScriptConstants.ATTRIBUTES);
                                    if (attributes != null)
                                        e.Attributes = this.ParseAttributes(attributes, e, report);

                                    this.ScopeElements[e.Name] = e;
                                }
                                else
                                {
                                    // Class member?
                                    Node classMemberDef = Helper.GetNode(memberDef, SafeScriptConstants.CLASS_MEMBER);
                                    if (classMemberDef != null)
                                    {
                                        Node attributes = Helper.GetNode(main, SafeScriptConstants.ATTRIBUTES);
                                        Node comments = Helper.GetNode(main, SafeScriptConstants.XMLCOMMENT);
                                        this.classMembers.Add(new UnparsedClassMember()
                                        {
                                            Node = classMemberDef,
                                            Use = use,
                                            Static = (Helper.GetNode(memberDef, SafeScriptConstants.STATIC) != null || !(this is AbstractType)),
                                            Attributes = attributes,
                                            Comments = comments
                                        });
                                    }
                                    else
                                    {
                                        // Constructor?
                                        Node ctor = Helper.GetNode(memberDef, SafeScriptConstants.CONSTRUCTOR);
                                        if (ctor != null)
                                        {
                                            if (this is Interface)
                                                report.Add(new ErrorEntry(ctor) { Type = ErrorType.CtorInInterface, Info = ctor.Name });
                                            else
                                                this.classMembers.Add(new UnparsedClassMember() { Node = ctor, Use = use });
                                        }
                                        else
                                            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
                                    }
                                }
                            }
                        }
                    }
                }
                // A statement which is part of the static initialization of this namespace?
                else
                {
                    Node statement = Helper.GetNode(main, SafeScriptConstants.STATEMENT);
                    if (statement != null)
                    {
                        if (this is Interface)
                        {
                            report.Add(new ErrorEntry(statement) { Info = this.Name, Type = ErrorType.StatementInInterface });
                        }
                        else
                        {
                            if (this.unparsedStatementsUsing != null && this.unparsedStatementsUsing != use)
                                report.Add(new ErrorEntry(statement) { Type = ErrorType.MultipleNamespaceInitializationCode, Info = this.Name });
                            else
                            {
                                this.unparsedStatementsUsing = use;
                                this.unparsedStatements.Add(statement);
                            }
                        }
                    }
                    else
                    {
                        System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
                    }
                }
                // Next & Loop
                main = Helper.GetNode(main, SafeScriptConstants.MAIN);
            }
        }

        private Namespace CreateNestedNamespaces(Node namespaceDef, ErrorReport report, Using use)
        {
            Node scopedIdentNode = Helper.GetNode(namespaceDef, SafeScriptConstants.SCOPED_IDENTIFIER);
            ScopedIdentifier scopedIdent = new ScopedIdentifier(scopedIdentNode);

            Namespace scope = this;
            // Skip the first name since this is equal to "this"
            for (int i = 0; i < scopedIdent.Count; ++i)
            {
                string ident = scopedIdent[i];
                object el = scope.GetScopeElement(ident);
                if (el == null)
                {
                    Namespace newscope = new Namespace() { Name = ident, ParentScope = scope };
                    scope.ScopeElements[newscope.Name] = newscope;
                    scope = newscope;
                }
                else if (el is Namespace)
                {
                    scope = el as Namespace;
                }
                else
                    // TODO: Proper error report
                    throw new Exception("The name " + ident + " is already used");
            }

            // The body of the nested namespace
            Node body = Helper.GetNode(namespaceDef, SafeScriptConstants.MAIN);
            if ( body == null )
                body = Helper.GetNode(namespaceDef, SafeScriptConstants.MAIN_WITH_USING);
            if (body != null)
                scope.ParseMain(body, report, use);

            return scope;
        }

        /// <summary>
        /// Resolve namespaces that have been included by the using directive.
        /// This function recursively visits all nested namespaces.
        /// </summary>
        /// <param name="report"></param>
        public virtual void ResolveMain(ErrorReport report)
        {
            // Resolve namespaces that have been included by "USING X"
            foreach (Using u in this.usingClauses)
                u.Resolve(report);

            // Recursively resolve nested namespaces and classes
            foreach (string name in this.ScopeElements.Keys.ToArray())
            {
                object obj = this.ScopeElements[name];
                if (obj is Namespace)
                {
                    (obj as Namespace).ResolveMain(report);
                }
            }
        }

        /// <summary>
        /// Checks that there are no loops in the inheritance hierarchy of classes and interfaces.
        /// This function recursively visits all nested namespaces.
        /// </summary>
        /// <param name="report"></param>
        public virtual void ParseInheritanceAndMembers(ErrorReport report)
        {
            foreach (string name in this.ScopeElements.Keys.ToArray())
            {
                Namespace nspace = this.ScopeElements[name] as Namespace;
                if (nspace != null)
                    nspace.ParseInheritanceAndMembers(report);
            }

            if ( !(this is AbstractType) )
                this.ParseClassMembers(report);
        }

        /// <summary>
        /// Parses members of the namespace which are not themselves classes or namespaces
        /// because they have already been parsed in ParseMain.
        /// This function recursively visits all nested namespaces.
        /// </summary>
        /// <param name="report"></param>
        public virtual void ParseClassMembers(ErrorReport report)
        {
            if (this.classMembers == null)
                return;

            foreach (UnparsedClassMember member in this.classMembers)
            {
                Node rest = Helper.GetNode(member.Node, SafeScriptConstants.CLASS_MEMBER_REST);

                if (Helper.GetToken(member.Node, SafeScriptConstants.VAR) != null)
                {
                    ParseUntypedMemberVariable(report, member);
                }
                else if (Helper.GetToken(member.Node, SafeScriptConstants.CTOR) != null)
                {
                    ParseConstructor(report, member);
                }
                else if (Helper.GetToken(member.Node, SafeScriptConstants.EVENT) != null)
                {
                    ParseMemberEvent(report, member);
                }
                else if ( rest != null )
                {
                    if (Helper.GetToken(rest, SafeScriptConstants.LEFT_PAREN) != null)
                    {
                        ParseMemberFunction(report, member);
                    }
                    else if (Helper.GetToken(rest, SafeScriptConstants.LEFT_SET) != null)
                    {
                        ParseProperty(report, member);
                    }
                    else if (Helper.GetToken(rest, SafeScriptConstants.SEMICOLON) != null)
                    {
                        ParseMemberVariable(report, member);
                    }
                }
                else
                    System.Diagnostics.Debug.Assert(false, "Unexpected grammar rule");
            }
            this.classMembers = null;
        }

        private void ParseMemberFunction(ErrorReport report, UnparsedClassMember member)
        {
            Node rest = Helper.GetNode(member.Node, SafeScriptConstants.CLASS_MEMBER_REST);
            System.Diagnostics.Debug.Assert(rest != null, "Unexpected grammar rule");
            Node classMemberDef = member.Node;

            string name = Helper.GetToken(rest, SafeScriptConstants.IDENTIFIER).Image;

            // TODO: Only allowed in classes and interfaces, in namespaces all member functions are by definition static
            Node parameterList = Helper.GetNode(rest, SafeScriptConstants.PARAMETER_LIST);
            Node returnTypeDef = Helper.GetNode(classMemberDef, SafeScriptConstants.TYPE_DEF);
            // Node statements = Helper.GetNode(classMemberDef, SafeScriptConstants.STATEMENT_LIST);            
            bool isOverride = Helper.GetToken(classMemberDef, SafeScriptConstants.OVERRIDE) != null;
            bool isAbstract = Helper.GetToken(classMemberDef, SafeScriptConstants.ABSTRACT) != null;
            bool isSealed = Helper.GetToken(classMemberDef, SafeScriptConstants.SEALED) != null;

            MemberFunction func = new MemberFunction() { Name = name, Node = member.Node, Using = member.Use, Owner = this };
            if (member.Attributes != null)
                func.Attributes = this.ParseAttributes(member.Attributes, func, report);

            ITypeDef rt = this.ParseTypeDef(returnTypeDef, member.Use, report);
            if (rt == null)
                return;
            if (!(rt is VoidType))
                func.ReturnType = new InstanceType() { Type = rt };
            else
                func.ReturnType = VoidType.Self;

            func.IsStatic = member.Static;
            func.IsOverride = isOverride;
            func.IsSealed = isSealed;
            func.IsAbstract = isAbstract;

            if (func.IsAbstract && this is Class && !(this as Class).IsAbstract)
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.AbstractFunctionInNonAbstractClass });
                return;
            }
            if (this is Interface && !func.IsAbstract)
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.NonAbstractFunctionInInterface });
                return;
            }
            if (this is Interface && func.IsStatic)
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.StaticFunctionInInterface });
                return;
            }
            if ( isOverride && !(this is Class))
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.OverridingOutsideClass });
            }

            // Function parameters?
            if (parameterList != null)
            {
                List<string> names = new List<string>();
                while (parameterList != null)
                {
                    Node paramTypeDef = Helper.GetNode(parameterList, SafeScriptConstants.TYPE_DEF);
                    ITypeDef paramType = this.ParseTypeDef(paramTypeDef, member.Use, report);
                    if (paramType == null)
                        return;
                    string paramName = Helper.GetToken(parameterList, SafeScriptConstants.IDENTIFIER).Image;
                    InstanceType it = new InstanceType() { Type = paramType };
                    func.Parameters.Add(new Parameter() { Name = paramName, Type = it });
                    func.ParameterTypes.Add(it);
                    if (names.Contains(paramName))
                        this.ReportDuplicateName(parameterList, report, paramName);
                    names.Add(paramName);
                    parameterList = Helper.GetNode(parameterList, SafeScriptConstants.PARAMETER_LIST);
                }
            }

            // Does a member of the same name already exist?
            object exist = this.GetScopeElement(name);
            if (exist != null)
            {
                if (exist is MemberFunction)
                {
                    MemberFunctionSet s = new MemberFunctionSet() { Name = name };
                    s.Add(exist as MemberFunction, report);
                    if (!s.Add(func, report))
                        return;
                    this.ScopeElements[name] = s;
                }
                else if (exist is MemberFunctionSet)
                {
                    if (!(exist as MemberFunctionSet).Add(func, report))
                        return;
                }
                else
                    this.ReportDuplicateName(classMemberDef, report, name);
            }
            else
                this.ScopeElements[name] = func;

            if (isSealed && func.IsStatic)
            {
                report.Add(new ErrorEntry(func.Node) { Info = name, Type = ErrorType.SealingStaticMember });
                return;
            }

            func.ComputeType(report);

            // Check that the override is correct
            if (isOverride)
            {
                if (func.IsStatic)
                {
                    report.Add(new ErrorEntry(func.Node) { Info = name, Type = ErrorType.OverridingStaticMember });
                    return;
                }

                MemberFunction baseFunc = null;
                Class cl = this as Class;
                System.Diagnostics.Debug.Assert(cl != null, "Programming error");
                // Loop over all base classes
                while (cl.BaseClass != null)
                {
                    cl = cl.BaseClass;
                    object obj = cl.GetScopeElement(name);
                    MemberFunction f = null;
                    if (obj is MemberFunctionSet)
                    {
                        MemberFunctionSet fs = obj as MemberFunctionSet;
                        f = fs.GetEquivalentFunction(func);
                    }
                    else if (obj is MemberFunction)
                        f = obj as MemberFunction;
                    if (f != null)
                    {
                        if (!f.IsEqualTo(func))
                        {
                            report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.SignatureOfOverrideDoesNotMatch });
                            return;
                        }
                        if (f.IsSealed)
                        {
                            report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.OverridingSealedFunction });
                            return;
                        }
                        baseFunc = f;
                        break;
                    }
                }
                // No function found to overload?
                if (baseFunc == null)
                {
                    report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.NoFunctionToOverride });
                    return;
                }
                func.OverridenFunction = baseFunc;
            }
        }

        private void ParseMemberEvent(ErrorReport report, UnparsedClassMember member)
        {
            Node classMemberDef = member.Node;
            string name = Helper.GetToken(classMemberDef, SafeScriptConstants.IDENTIFIER).Image;
            if (this is Interface)
            {
                report.Add(new ErrorEntry(member.Node) { Info = name, Type = ErrorType.VariableInInterface });
                return;
            }
            if (this.GetScopeElement(name) != null)
                this.ReportDuplicateName(classMemberDef, report, name);
            Node typedef = Helper.GetNode(classMemberDef, SafeScriptConstants.TYPE_DEF);
            ITypeDef type = this.ParseTypeDef(typedef, member.Use, report);
            if (type == null)
                return;
            if (!(type is VoidType))
                type = new InstanceType() { Type = type };
            MemberEvent e = new MemberEvent() { Name = name, EventType = type, Node = member.Node, Using = member.Use, Owner = this };
            if (member.Attributes != null)
                e.Attributes = this.ParseAttributes(member.Attributes, e, report);
            e.IsStatic = member.Static;
            this.ScopeElements[name] = e;
        }

        private void ParseMemberVariable(ErrorReport report, UnparsedClassMember member)
        {
            Node classMemberDef = member.Node;
            Node rest = Helper.GetNode(member.Node, SafeScriptConstants.CLASS_MEMBER_REST);
            System.Diagnostics.Debug.Assert(rest != null, "Unexpected grammar rule");

            string name = Helper.GetToken(rest, SafeScriptConstants.IDENTIFIER).Image;
            if (this is Interface)
            {
                report.Add(new ErrorEntry(member.Node) { Info = name, Type = ErrorType.VariableInInterface });
                return;
            }
            if (this.GetScopeElement(name) != null)
                this.ReportDuplicateName(classMemberDef, report, name);
            ITypeDef type = null;
            Node typedef = Helper.GetNode(classMemberDef, SafeScriptConstants.TYPE_DEF);
            type = this.ParseTypeDef(typedef, member.Use, report);
            // Error?
            if (type == null)
                return;
            type = new InstanceType() { Type = type };

            MemberVariable v = new MemberVariable() { Name = name, Type = type, Node = member.Node, Using = member.Use, Owner = this };
            if (member.Attributes != null)
                v.Attributes = this.ParseAttributes(member.Attributes, v, report);
            v.IsStatic = member.Static;
            this.ScopeElements[name] = v;
        }

        private void ParseUntypedMemberVariable(ErrorReport report, UnparsedClassMember member)
        {
            Node classMemberDef = member.Node;
            string name = Helper.GetToken(classMemberDef, SafeScriptConstants.IDENTIFIER).Image;
            if (this is Interface)
            {
                report.Add(new ErrorEntry(member.Node) { Info = name, Type = ErrorType.VariableInInterface });
                return;
            }
            if (this.GetScopeElement(name) != null)
                this.ReportDuplicateName(classMemberDef, report, name);
            MemberVariable v = new MemberVariable() { Name = name, Type = null, Node = member.Node, Using = member.Use, Owner = this };
            if (member.Attributes != null)
                v.Attributes = this.ParseAttributes(member.Attributes, v, report);
            v.IsStatic = member.Static;
            this.ScopeElements[name] = v;
        }

        private void ParseProperty(ErrorReport report, UnparsedClassMember member)
        {
            Node classMemberDef = member.Node;
            Node rest = Helper.GetNode(member.Node, SafeScriptConstants.CLASS_MEMBER_REST);
            System.Diagnostics.Debug.Assert(rest != null, "Unexpected grammar rule");

            string name = Helper.GetToken(rest, SafeScriptConstants.IDENTIFIER).Image;
            bool isOverride = Helper.GetToken(classMemberDef, SafeScriptConstants.OVERRIDE) != null;
            bool isSealed = Helper.GetToken(classMemberDef, SafeScriptConstants.SEALED) != null;
            bool isAbstract = Helper.GetToken(classMemberDef, SafeScriptConstants.ABSTRACT) != null;
            if (this.GetScopeElement(name) != null)
                this.ReportDuplicateName(classMemberDef, report, name);
            Node typedef = Helper.GetNode(classMemberDef, SafeScriptConstants.TYPE_DEF);
            ITypeDef type = this.ParseTypeDef(typedef, member.Use, report);
            if (type == null)
                return;
            type = new InstanceType() { Type = type };
            Property p = new Property() { Name = name, Type = type, Node = member.Node, Using = member.Use, Owner = this };
            p.IsStatic = member.Static;
            //p.IsOverride = isOverride;
            //p.IsSealed = isSealed;
            //p.IsAbstract = isAbstract;

            if (this is Interface && !isAbstract)
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.NonAbstractPropertyInInterface });
                return;
            }
            if (this is Interface && p.IsStatic)
            {
                report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.StaticPropertyInInterface });
                return;
            }

            this.ScopeElements[name] = p;

            Node propertyGet = Helper.GetNode(rest, SafeScriptConstants.PROPERTY_GET);
            Node propertySet = Helper.GetNode(rest, SafeScriptConstants.PROPERTY_SET);
            if (propertyGet != null)
            {
                p.Getter = new PropertyGetter() { Name = name + "_get", IsAbstract = isAbstract, IsOverride = isOverride, IsSealed = isSealed, Property = p, Owner = this };
                p.Getter.StatementsNode = Helper.GetNode(propertyGet, SafeScriptConstants.STATEMENT_LIST);
            }
            if (propertySet != null)
            {
                p.Setter = new PropertySetter() { Name = name + "_set", IsAbstract = isAbstract, IsOverride = isOverride, IsSealed = isSealed, Property = p, Owner = this };
                p.Setter.StatementsNode = Helper.GetNode(propertySet, SafeScriptConstants.STATEMENT_LIST);
            }
            if (isAbstract && p.Setter != null && p.Setter.Statements != null )
                report.Add(new ErrorEntry(p.Node) { Type = ErrorType.AbstractPropertyWithImplementation });
            if (isAbstract && p.Getter != null && p.Getter.Statements != null)
                report.Add(new ErrorEntry(p.Node) { Type = ErrorType.AbstractPropertyWithImplementation });
            if (propertyGet == null && propertySet == null)
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.NeitherGetterNotSetter });
            if( p.Setter != null && p.Getter != null && p.Setter.Statements != null && p.Getter.Statements == null )
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.MixedDefaultGetSetWithCustomGetSet });
            if (p.Setter != null && p.Getter != null && p.Setter.Statements == null && p.Getter.Statements != null)
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.MixedDefaultGetSetWithCustomGetSet });
            if (isSealed && p.IsStatic)
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.SealingStaticMember });
            if (isAbstract && p.IsStatic)
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.AbstractStaticMember });
            if (isAbstract && this is Class && !(this as Class).IsAbstract)
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.AbstractPropertyInNonAbstractClass });
            if (isAbstract && !(this is Class) && !(this is Interface))
                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.AbstractPropertyInNonAbstractClass });

            // TODO: Visibiliy of getter and setter

            // Check that the override is correct
            if (isOverride)
            {
                if (p.IsStatic)
                {
                    report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.OverridingStaticMember });
                    return;
                }

                PropertyGetter basePropGet = null;
                PropertySetter basePropSet = null;
                Class cl = this as Class;
                if (cl != null)
                {
                    while (cl.BaseClass != null)
                    {
                        cl = cl.BaseClass;
                        Property p2 = cl.GetScopeElement(name) as Property;
                        if (p2 != null)
                        {
                            if (!p2.Type.IsEqualTo(p.Type))
                            {
                                report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.SignatureOfOverrideDoesNotMatch });
                                return;
                            }
                            // Does this property feature a getter?
                            if (propertyGet != null && p2.Getter != null)
                            {
                                if (p2.Getter.IsSealed)
                                {
                                    report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.OverridingSealedProperty });
                                    return;
                                }
                                basePropGet = p2.Getter;
                                p.Getter.OverridenGetter = basePropGet;
                            }
                            // Does this property feature a setter?
                            if (propertySet != null && p2.Setter != null)
                            {
                                if (p2.Setter.IsSealed)
                                {
                                    report.Add(new ErrorEntry(p.Node) { Info = name, Type = ErrorType.OverridingSealedProperty });
                                    return;
                                }                                
                                basePropSet = p2.Setter;
                                p.Setter.OverridenSetter = basePropSet;
                            }
                        }
                    }
                }
                // No property getter found to overload?
                if (propertyGet != null && basePropGet == null)
                {
                    report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.NoPropertyGetterToOverride });
                    return;
                }
                // No property getter found to overload?
                if (propertySet != null && basePropSet == null)
                {
                    report.Add(new ErrorEntry(classMemberDef) { Info = name, Type = ErrorType.NoPropertySetterToOverride });
                    return;
                }
            }

            if (member.Attributes != null)
                p.Attributes = this.ParseAttributes(member.Attributes, p, report);

        }

        private void ParseConstructor(ErrorReport report, UnparsedClassMember member)
        {
            Node classMemberDef = member.Node;
            // Constructors are allowed in classes only.
            if (!(this is Class))
            {
                report.Add(new ErrorEntry(classMemberDef) { Type = ErrorType.CtorOutsideClass });
                return;
            }

            string name = "ctor";
            Node parameterList = Helper.GetNode(classMemberDef, SafeScriptConstants.PARAMETER_LIST);
            Node statements = Helper.GetToken(classMemberDef, SafeScriptConstants.STATEMENT_LIST);

            Constructor ctor = new Constructor() { Name = name, ReturnType = new InstanceType() { Type = (this as Class) }, Node = member.Node, Using = member.Use, Owner = this };

            // Function parameters?
            if (parameterList != null)
            {
                List<string> names = new List<string>();
                while (parameterList != null)
                {
                    Node paramTypeDef = Helper.GetNode(parameterList, SafeScriptConstants.TYPE_DEF);
                    ITypeDef paramType = this.ParseTypeDef(paramTypeDef, member.Use, report);
                    string paramName = Helper.GetToken(parameterList, SafeScriptConstants.IDENTIFIER).Image;
                    InstanceType it = new InstanceType() { Type = paramType };
                    ctor.Parameters.Add(new Parameter() { Name = paramName, Type = it });
                    ctor.ParameterTypes.Add(it);
                    if (names.Contains(paramName))
                        this.ReportDuplicateName(parameterList, report, paramName);
                    names.Add(paramName);
                    parameterList = Helper.GetNode(parameterList, SafeScriptConstants.PARAMETER_LIST);
                }
            }

            // Super parameters?
            //Node superArgs = Helper.GetNode(member.Node, SafeScriptConstants.LIST_CONTENT);
            //while (superArgs != null)
            //{
            //    Operator arg = this.ParseInlineFunction(superArgs[0], report, member.Use);
            //    if (arg == null)
            //        return;
            //    ctor.SuperParameters.Add(arg);
            //    superArgs = Helper.GetNode(superArgs, SafeScriptConstants.LIST_CONTENT);
            //}

            ctor.ComputeType(report);

            // Perhaps multiple constructors exist. If so -> create a MemberFunctionSet
            object otherCtor = this.GetScopeElement(name);
            if (otherCtor != null)
            {
                if (otherCtor is MemberFunctionSet)
                    (otherCtor as MemberFunctionSet).Add(ctor, report);
                else
                {
                    MemberFunctionSet mfs = new MemberFunctionSet() { Name = "ctor" };
                    mfs.Add(otherCtor as Constructor, report);
                    mfs.Add(ctor, report);
                    this.ScopeElements[name] = mfs;
                }
            }
            else
                this.ScopeElements[name] = ctor;
        }

        private MemberAttribute[] ParseAttributes(Node attributes, object context, ErrorReport report)
        {
            List<MemberAttribute> attribs = new List<MemberAttribute>();

            while (attributes != null)
            {
                string name = Helper.GetToken(attributes, SafeScriptConstants.IDENTIFIER).Image;
                List<object> vals = new List<object>();
                Node values = Helper.GetNode(attributes, SafeScriptConstants.ATTRIBUTE_VALUES);
                if (values != null)
                {
                    Node vlist = Helper.GetNode(values, SafeScriptConstants.ATTRIBUTE_VALUE_LIST);
                    while (vlist != null)
                    {
                        Node val = vlist[0];
                        switch ((SafeScriptConstants)val.Id)
                        {
                            case SafeScriptConstants.STRING:
                                {
                                    var str = (val as Token).Image;
                                    // TODO: proper escape
                                    vals.Add(str.Substring(1, str.Length - 2));
                                }
                                break;
                            case SafeScriptConstants.TRUE:
                                vals.Add(true);
                                break;
                            case SafeScriptConstants.FALSE:
                                vals.Add(false);
                                break;
                        }
                        vlist = Helper.GetNode(vlist, SafeScriptConstants.ATTRIBUTE_VALUE_LIST);
                    }
                }
                MemberAttribute ma = MemberAttribute.Create(attributes, name, vals.ToArray(), context, report);
                if ( ma != null )
                    attribs.Add(ma);
                attributes = Helper.GetNode(attributes, SafeScriptConstants.ATTRIBUTES);
            }

            return attribs.ToArray();
        }

        internal ITypeDef ParseTypeDef(Node typedef, Using use, ErrorReport report)
        {
            Node atom = Helper.GetNode(typedef, SafeScriptConstants.TYPE_DEF_ATOM);
            ITypeDef result = this.ParseTypeDefAtom(atom, use, report);

            // A function type of the form "bool -> int" or a binding of the form "bool => int"
            Node rest = Helper.GetNode(typedef, SafeScriptConstants.TYPE_DEF_REST);
            if (rest != null)
            {
                if (rest[0].Id == (int)SafeScriptConstants.BIND)
                {
                    BindingTypeDef bind = new BindingTypeDef();
                    if (!(result is VoidType))
                        bind.ParameterTypes.Add(new InstanceType() { Type = result });
                    ITypeDef returnType = this.ParseTypeDefAtom(rest[1], use, report);
                    if (returnType == null)
                        return null;
                    // Return type other than void?
                    if (!(returnType is VoidType))
                        bind.ReturnType = new InstanceType() { Type = returnType };
                    else
                        bind.ReturnType = VoidType.Self;
                    return bind;
                }
                else
                {
                    FunctionTypeDef func = new FunctionTypeDef();
                    if (!(result is VoidType))
                        func.ParameterTypes.Add(new InstanceType() { Type = result });
                    ITypeDef returnType = this.ParseTypeDefAtom(rest[1], use, report);
                    if (returnType == null)
                        return null;
                    // Return type other than void?
                    if (!(returnType is VoidType))
                        func.ReturnType = new InstanceType() { Type = returnType };
                    else
                        func.ReturnType = VoidType.Self;
                    return func;
                }
            }

            return result;
        }

        private ITypeDef ParseTypeDefAtom(Node atom, Using use, ErrorReport report)
        {
            Node simple = Helper.GetNode(atom, SafeScriptConstants.SIMPLE_TYPE_DEF);
            if (simple != null)
            {
                return this.ParseSimpleTypeDef(use, report, simple);
            }
            Node func = Helper.GetNode(atom, SafeScriptConstants.FUNCTION_TYPE_DEF);
            if (func != null)
            {
                Node parameterList = Helper.GetNode(func, SafeScriptConstants.TYPE_DEF_LIST);
                Node returnTypeDef = Helper.GetNode(func, SafeScriptConstants.TYPE_DEF);

                FunctionTypeDef funcType = new FunctionTypeDef();

                ITypeDef returnType = null;
                if (returnTypeDef != null)
                    returnType = this.ParseTypeDef(returnTypeDef, use, report);
                // Return type other than void?
                if (returnType != null && !(returnType is VoidType))
                    funcType.ReturnType = new InstanceType() { Type = returnType };
                else
                    funcType.ReturnType = VoidType.Self;

                // Function parameters?
                if (parameterList != null)
                {
                    while (parameterList != null)
                    {
                        Node paramTypeDef = Helper.GetNode(parameterList, SafeScriptConstants.TYPE_DEF);
                        ITypeDef paramType = this.ParseTypeDef(paramTypeDef, use, report);
                        funcType.ParameterTypes.Add(new InstanceType() { Type = paramType });
                        parameterList = Helper.GetNode(parameterList, SafeScriptConstants.TYPE_DEF_LIST);
                    }
                }
                return funcType;
            }
            Node nested = Helper.GetNode(atom, SafeScriptConstants.NESTED_TYPE_DEF);
            if (nested != null)
            {
                return this.ParseTypeDef(Helper.GetNode(nested, SafeScriptConstants.TYPE_DEF), use, report);
            }
            return null;
        }

        private AbstractType ParseSimpleTypeDef(Using use, ErrorReport report, Node simple)
        {
            Namespace nspace = this;
            Node first = simple;
            do
            {
                // Get the name of the type or namespace
                string name = Helper.GetToken(simple, SafeScriptConstants.IDENTIFIER).Image;

                // Count the template parameters
                Node templ = Helper.GetNode(simple, SafeScriptConstants.TEMPLATE_ARGS);
                if (templ != null)
                {
                    int templParamCount = 0;
                    Node typelist = Helper.GetNode(templ, SafeScriptConstants.TYPE_DEF_LIST);
                    while (typelist != null)
                    {
                        templParamCount++;
                        typelist = Helper.GetNode(typelist, SafeScriptConstants.TYPE_DEF_LIST);
                    }

                    name = name + "#" + templParamCount.ToString();
                }

                nspace = nspace.FindNamespace(name, use, first == simple);
                // AbstractType typedef = this.FindAbstractType(ident, use);
                if ( nspace == null )
                {
                    report.Add(new ErrorEntry(simple)
                    {
                        Info = name,
                        Type = ErrorType.UnknownType
                    });
                    return null;
                }
                AbstractType typedef = nspace as AbstractType;

                // Parse and check the template parameters
                templ = Helper.GetNode(simple, SafeScriptConstants.TEMPLATE_ARGS);
                if (templ != null)
                {
                    // If this is only a namespace and no type -> template parameters are wrong
                    if (typedef == null)
                    {
                        report.Add(new ErrorEntry(simple)
                        {
                            Info = name,
                            Type = ErrorType.UnknownType
                        });
                        return null;
                    }

                    Dictionary<TemplateParameterType, ITypeDef> typeargs = new Dictionary<TemplateParameterType, ITypeDef>();

                    int i = 0;
                    Node typelist = Helper.GetNode(templ, SafeScriptConstants.TYPE_DEF_LIST);
                    while (typelist != null)
                    {
                        // Number of template parameters is not ok?
                        if (typedef.TemplateParameters.Count <= i)
                        {
                            report.Add(new ErrorEntry(simple) { Info = name, Type = ErrorType.UnknownClass });
                            return null;
                        }

                        ITypeDef ta = this.ParseTypeDef(Helper.GetNode(typelist, SafeScriptConstants.TYPE_DEF), use, report);
                        if (ta == null)
                            return null;

                        // Does the type match with the template parameter?
                        // TODO: ctor checking
                        if (ta.CanBeAssignedTo(typedef.TemplateParameters[i]))
                        {
                            report.Add(new ErrorEntry(typelist) { Type = ErrorType.IncompatibleTypes });
                            return null;
                        }

                        // t.TemplateArguments.Add(ta);
                        typeargs[typedef.TemplateParameters[i]] = ta;

                        typelist = Helper.GetNode(typelist, SafeScriptConstants.TYPE_DEF_LIST);
                        i++;
                    }

                    typedef = Compiler.CurrentCompiler.GetTemplateInstance(typedef, typeargs);
                    nspace = typedef;
                }

                simple = Helper.GetNode(simple, SafeScriptConstants.SIMPLE_TYPE_DEF);
                if (simple == null && typedef == null)
                {
                    report.Add(new ErrorEntry(simple)
                    {
                        Info = name,
                        Type = ErrorType.UnknownType
                    });
                    return null;
                }
            } while (simple != null);

            return nspace as AbstractType;
        }

        public void ParseMemberVariableInitializtion(ErrorReport report)
        {
            this.parsingStaticCode = true;
            // Recursion
            foreach (string name in this.ScopeElements.Keys.ToArray())
            {
                object obj = this.ScopeElements[name];
                Namespace nspace = obj as Namespace;
                if (nspace != null)
                {
                    nspace.ParseMemberVariableInitializtion(report);
                    continue;
                }
                MemberVariable memberVar = obj as MemberVariable;
                if (memberVar != null)
                {
                    if (memberVar.Node != null)
                    {
                        Node rest = Helper.GetNode(memberVar.Node, SafeScriptConstants.CLASS_MEMBER_REST);
                        if (rest == null)
                            rest = memberVar.Node;
                        Node inline = Helper.GetNode(rest, SafeScriptConstants.INLINE_FUNCTION);
                        if (inline != null)
                            memberVar.InitialValue = this.ParseInlineFunction(inline, report, memberVar.Using);
                    }
                    continue;
                }
            }
        }

        public void ParseStatements(ErrorReport report)
        {
            if (this.unparsedStatementsUsing != null)
            {
                this.parsingStaticCode = true;
                this.unparsedStatementsUsing.Stack.Push(new Dictionary<string, object>());
                foreach (Node statement in this.unparsedStatements)
                {
                    if (this.Statements == null)
                        this.Statements = new StatementList();
                    this.Statements.Add(this.ParseStatement(statement, report, this.unparsedStatementsUsing));
                }
                this.unparsedStatementsUsing.Stack.Pop();
            }

            // Iterate over all members of this namespace and parse their statements
            foreach (string name in this.ScopeElements.Keys.ToArray())
            {
                object obj = this.ScopeElements[name];
                this.ParseMemberStatements(obj, report);
            }
        }

        public void ParseMemberStatements(object obj, ErrorReport report)
        {
            Namespace nspace = obj as Namespace;
            if (nspace != null)
            {
                nspace.ParseStatements(report);
                return;
            }
            Property prop = obj as Property;
            if (prop != null)
            {
                this.parsingStaticCode = prop.IsStatic;
                if (prop.Getter != null && prop.Getter.StatementsNode != null)
                {
                    this.parsingContext = prop.Getter;
                    this.parsingContextHasReturns = false;
                    prop.Getter.Statements = this.ParseStatementList(prop.Getter.StatementsNode, report, prop.Using);
                }
                this.parsingPropertySetType = prop.Type;
                if (prop.Setter != null && prop.Setter.StatementsNode != null)
                {
                    this.parsingContext = prop.Setter;
                    this.parsingContextHasReturns = false;
                    prop.Setter.Statements = this.ParseStatementList(prop.Setter.StatementsNode, report, prop.Using);
                }
                this.parsingContext = null;
                this.parsingPropertySetType = null;
                return;
            }
            MemberVariable memberVar = obj as MemberVariable;
            if (memberVar != null)
            {
                // Do nothing by intention. This has already been computed before to infere the type of the initialization value
                return;
            }
            Constructor ctor = obj as Constructor;
            if (ctor != null)
            {
                // Add all parameters to the scope
                Dictionary<string, object> scope = new Dictionary<string, object>();
                foreach (Parameter p in ctor.Parameters)
                    scope[p.Name] = p;
                ctor.Using.Stack.Push(scope);

                Node listContent = Helper.GetNode(ctor.Node, SafeScriptConstants.LIST_CONTENT);
                this.parsingStaticCode = true; // Instance variables are not yet accessible
                // Parameters for calling super?
                while (listContent != null)
                {
                    Operator super = this.ParseInlineFunction(listContent[0], report, ctor.Using);
                    // In case of an error and to prevent a crash
                    if (super != null)
                        ctor.SuperParameters.Add(super);
                    listContent = Helper.GetNode(listContent, SafeScriptConstants.LIST_CONTENT);
                }

                ctor.ComputeSuperType(report);

                // Statements
                this.parsingStaticCode = false;
                this.parsingContext = ctor;
                this.parsingContextHasReturns = false;
                Node body = Helper.GetNode(ctor.Node, SafeScriptConstants.MEMBER_FUNCTION_BODY);
                ctor.Statements = this.ParseStatementList(Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST), report, ctor.Using);
                ctor.Using.Stack.Pop();
                this.parsingContext = null;
                return;
            }
            MemberFunction func = obj as MemberFunction;
            if (func != null)
            {
                if (!(func is BuiltinMemberFunction) && !func.IsAbstract)
                {
                    this.parsingStaticCode = func.IsStatic;
                    Dictionary<string, object> scope = new Dictionary<string, object>();
                    foreach (Parameter p in func.Parameters)
                        scope[p.Name] = p;
                    func.Using.Stack.Push(scope);

                    // Statements
                    this.parsingContext = func;
                    this.parsingContextHasReturns = false;
                    Node rest = Helper.GetNode(func.Node, SafeScriptConstants.CLASS_MEMBER_REST);
                    Node body = Helper.GetNode(rest, SafeScriptConstants.MEMBER_FUNCTION_BODY);
                    Node statements = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                    if (statements != null)
                        func.Statements = this.ParseStatementList(statements, report, func.Using);
                    func.Using.Stack.Pop();
                    this.parsingContext = null;
                }
                return;
            }
            MemberFunctionSet funcset = obj as MemberFunctionSet;
            if (funcset != null)
            {
                foreach (MemberFunction f in funcset.Functions)
                {
                    if (f.Owner != this)
                        continue;
                    this.ParseMemberStatements(f, report);
                }
                return;
            }
        }

        private StatementList ParseStatementList(Node list, ErrorReport report, Using use)
        {
            Dictionary<string, object> scope = new Dictionary<string, object>();
            use.Stack.Push(scope);
            StatementList result = new StatementList();
            while (list != null)
            {
                Node statement = Helper.GetNode(list, SafeScriptConstants.STATEMENT);
                Statement s = this.ParseStatement(statement, report, use);
                if ( s != null )
                    result.Add(s);
                list = Helper.GetNode(list, SafeScriptConstants.STATEMENT_LIST);
            }
            use.Stack.Pop();
            return result;
        }

        private Statement ParseStatement(Node statement, ErrorReport report, Using use)
        {
            switch ((SafeScriptConstants)statement[0].Id)
            {
                case SafeScriptConstants.ASSIGNMENT:
                    {
                        Node binding = statement[0];
                        Operator op = this.ParseAssignment(binding, report, use, true);
                        if (op == null)
                            return null;
                        ExpressionStatement s = new ExpressionStatement() { Operator = op, Node = statement };
                        s.ComputeType(report);
                        return s;
                    }
                case SafeScriptConstants.IF_CLAUSE:
                    return this.ParseIf(statement[0], report, use);
                case SafeScriptConstants.FOR_CLAUSE:
                    {
                        Node fs = statement[0];
                        ForStatement f = new ForStatement() { Node = fs };
                        Dictionary<string, object> scope = new Dictionary<string, object>();
                        use.Stack.Push(scope);
                        try
                        {
                            Node init = Helper.GetNode(fs, SafeScriptConstants.FOR_CLAUSE1);
                            if (init != null)
                                f.Initialization = this.ParseVar(init, report, use);
                            Node cond = Helper.GetNode(fs, SafeScriptConstants.FOR_CLAUSE2);
                            if (cond != null)
                            {
                                f.Condition = this.ParseAssignment(cond[0], report, use, true);
                                if (f.Condition == null)
                                    return null;
                                ITypeDef ctype = f.Condition.ComputeType(BoolType.Self, false, report);
                                if (ctype != null)
                                    if (!(ctype is InstanceType) || !((ctype as InstanceType).Type.IsEqualTo(BoolType.Self)))
                                        report.Add(new ErrorEntry(f.Condition.Node) { Type = ErrorType.ConditionMustBeBool });
                            }
                            Node incr = Helper.GetNode(fs, SafeScriptConstants.FOR_CLAUSE3);
                            if (incr != null)
                            {
                                f.Increment = this.ParseAssignment(incr[0], report, use, true);
                                if (f.Increment == null)
                                    return null;
                                f.Increment.ComputeType(BoolType.Self, false, report);
                            }

                            // Parse statements
                            Node body = Helper.GetNode(fs, SafeScriptConstants.CLAUSE_BODY);
                            Node statementList = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                            if (statementList != null)
                                f.Statements = this.ParseStatementList(statementList, report, use);
                            else
                                f.Statements = new StatementList();
                        }
                        finally
                        {
                            use.Stack.Pop();
                        }
                        return f;
                    }
                case SafeScriptConstants.WHILE_CLAUSE:
                case SafeScriptConstants.DO_WHILE_CLAUSE:
                    {
                        Node whileStatement = statement[0];
                        WhileStatement s = new WhileStatement() { Node = whileStatement };
                        s.Condition = this.ParseAssignment(Helper.GetNode(whileStatement, SafeScriptConstants.ASSIGNMENT), report, use, true);
                        Node body = Helper.GetNode(whileStatement, SafeScriptConstants.CLAUSE_BODY);
                        Node statementList = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                        if (statementList != null)
                            s.Statements = this.ParseStatementList(statementList, report, use);
                        else
                            s.Statements = new StatementList();
                        if (whileStatement.Id == (int)SafeScriptConstants.DO_WHILE_CLAUSE)
                            s.IsDoWhileLoop = true;
                        ITypeDef ctype = s.Condition.ComputeType(BoolType.Self, false, report);
                        if ( ctype != null)
                            if (!(ctype is InstanceType) || !((ctype as InstanceType).Type.IsEqualTo(BoolType.Self)))
                                report.Add(new ErrorEntry(s.Condition.Node) { Type = ErrorType.ConditionMustBeBool });
                        return s;
                    }
                case SafeScriptConstants.SWITCH_CLAUSE:
                    // TODO
                    return null;
                case SafeScriptConstants.FOREACH_CLAUSE:
                    {
                        Node forStatement = statement[0];
                        ForEachStatement f = new ForEachStatement() { Node = forStatement };
                        
                        f.Content = this.ParseLogicalOr(Helper.GetNode(forStatement, SafeScriptConstants.LOGICAL_OR), report, use);
                        if (f.Content == null)
                            return null;
                        f.Var = new VarStatement() { Node = forStatement, Owner = this.parsingContext };
                        this.RegisterVariable(f.Var);
                        f.Var.Name = Helper.GetToken(forStatement, SafeScriptConstants.IDENTIFIER).Image;
                        Node t = Helper.GetNode(forStatement, SafeScriptConstants.TYPE_DEF);
                        if ( t != null )
                            f.Var.Type = new InstanceType() { Type = this.ParseTypeDef(t, use, report) };
                        f.ComputeType(report);

                        // Parse the statements
                        Node body = Helper.GetNode(forStatement, SafeScriptConstants.CLAUSE_BODY);
                        Node statementList = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                        if (statementList != null)
                        {
                            Dictionary<string, object> scope = new Dictionary<string, object>();
                            scope[f.Var.Name] = f.Var;
                            use.Stack.Push(scope);
                            f.Statements = this.ParseStatementList(statementList, report, use);
                            use.Stack.Pop();
                        }
                        else
                            f.Statements = new StatementList();

                        return f;
                    }
                case SafeScriptConstants.TRY_CLAUSE:
                    // TODO
                    return null;
                case SafeScriptConstants.VAR:
                    return this.ParseVar(statement, report, use);
                case SafeScriptConstants.RETURN:
                    {
                        if ((this.parsingContext is MemberFunction && (this.parsingContext as MemberFunction).IsIterator == true) ||
                            (this.parsingContext is FunctionOperator && (this.parsingContext as FunctionOperator).IsIterator == true))
                        {
                            report.Add(new ErrorEntry(statement) { Type = ErrorType.ReturnInIterator });
                            return null;
                        }
                        this.parsingContextHasReturns = true;

                        Operator op = statement.Count == 3 ? this.ParseAssignment(statement[1], report, use, true) : null;
                        ReturnStatement s = new ReturnStatement() { Operator = op, Context = this.parsingContext, Node = statement };
                        s.ComputeType(report);
                        return s;                        
                    }
                case SafeScriptConstants.YIELD:
                    {
                        if (this.parsingContext is MemberFunction)
                            (this.parsingContext as MemberFunction).IsIterator = true;
                        else if (this.parsingContext is FunctionOperator)
                            (this.parsingContext as FunctionOperator).IsIterator = true;

                        if (this.parsingContextHasReturns)
                        {
                            report.Add(new ErrorEntry(statement) { Type = ErrorType.YieldInFunction });
                            return null;
                        }

                        if (statement[1].Id == (int)SafeScript.SafeScriptConstants.RETURN)
                        {
                            YieldReturnStatement s = new YieldReturnStatement() { Operator = this.ParseAssignment(statement[2], report, use, true), Context = this.parsingContext, Node = statement };
                            s.ComputeType(report);
                            return s;
                        }
                        return new YieldBreakStatement() { Node = statement };
                    }
                case SafeScriptConstants.THROW:
                    {
                        Operator op = this.ParseAssignment(statement[1], report, use, true);
                        if (op == null)
                            return null;
                        ThrowStatement s = new ThrowStatement() { Operator = op, Node = statement };
                        s.ComputeType(report);
                        return s;
                    }
                case SafeScriptConstants.BREAK:
                    return new BreakStatement() { Node = statement };
                case SafeScriptConstants.CONTINUE:
                    return new ContinueStatement() { Node = statement };
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammer rule");
            return null;
        }

        private IfStatement ParseIf(Node ifStatement, ErrorReport report, Using use)
        {
            if (Helper.GetToken(ifStatement, SafeScriptConstants.IF) != null)
            {
                // Parse the if statement
                IfStatement s = new IfStatement() { Node = ifStatement };
                s.Condition = this.ParseAssignment(Helper.GetNode(ifStatement, SafeScriptConstants.ASSIGNMENT), report, use, true);
                // Some error?
                if (s.Condition == null)
                    return null;
                Node body = Helper.GetNode(ifStatement, SafeScriptConstants.CLAUSE_BODY);
                Node statementList = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                if (statementList != null)
                    s.Statements = this.ParseStatementList(statementList, report, use);
                else
                    s.Statements = new StatementList();

                // Check the type of the condition
                ITypeDef ctype = s.Condition.ComputeType(BoolType.Self, false, report);
                if (ctype != null)
                    if (!(ctype is InstanceType) || !((ctype as InstanceType).Type.IsEqualTo(BoolType.Self)))
                        report.Add(new ErrorEntry(s.Condition.Node) { Type = ErrorType.ConditionMustBeBool });

                // Parse the else tree
                Node ifrest = Helper.GetNode(ifStatement, SafeScriptConstants.IF_REST);
                if (ifrest != null)
                    s.Else = this.ParseIf(ifrest, report, use);
                return s;
            }
            else
            {
                IfStatement s = new IfStatement() { Node = ifStatement };
                Node body = Helper.GetNode(ifStatement, SafeScriptConstants.CLAUSE_BODY);
                Node statementList = Helper.GetNode(body, SafeScriptConstants.STATEMENT_LIST);
                if (statementList != null)
                    s.Statements = this.ParseStatementList(statementList, report, use);
                else
                    s.Statements = new StatementList();
                return s;
            }
        }

        private VarStatement ParseVar(Node statement, ErrorReport report, Using use)
        {
            string name = Helper.GetToken(statement, SafeScriptConstants.IDENTIFIER).Image;
            if ( use.Stack.Peek().ContainsKey(name) )
                this.ReportDuplicateName(statement, report, name);
            ITypeDef type = null;
            Node typedef = Helper.GetNode(statement, SafeScriptConstants.TYPE_DEF);
            if (typedef != null)
                type = this.ParseTypeDef(typedef, use, report);
            if (type != null && !(type is InstanceType))
                type = new InstanceType() { Type = type };
            VarStatement v = new VarStatement() { Name = name, Type = type, Node = statement, Owner = this.parsingContext };
            this.RegisterVariable(v);

            Node initialValue = Helper.GetNode(statement, SafeScriptConstants.ASSIGNMENT);
            if (initialValue != null)
                v.InitialValue = this.ParseAssignment(initialValue, report, use, true);

            v.ComputeType(report);

            // Add the variable to the list of available identifiers
            use.Stack.Peek()[name] = v;

            return v;
        }

        private Operator ParseAssignment(Node assignment, ErrorReport report, Using use, bool injectBindingObjectOperator)
        {
            Operator op = this.ParseBinding(assignment[0], report, use, injectBindingObjectOperator);
            if (op == null)
                return null;
            if (assignment.Count == 1)
                return op;
            Node rest = assignment[1];
            Operator op2 = this.ParseAssignment(rest[1], report, use, injectBindingObjectOperator);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = assignment };
        }

        private Operator ParseBinding(Node binding, ErrorReport report, Using use, bool injectBindingObjectOperator)
        {
            bool isbinding = this.parsingExpressionIsBinding;

            Operator op = this.ParseInlineFunction(binding[0], report, use);
            if (op == null)
                return null;
            if (this.parsingExpressionIsBinding && injectBindingObjectOperator)
            {
                op = new BindingObjectOperator() { Node = binding, Operator = op };
                this.parsingExpressionIsBinding = isbinding;
            }
            if (binding.Count == 1)
                return op;
            Node rest = binding[1];
            Operator op2 = this.ParseBinding(rest[1], report, use, true);
            if (op2 == null)
                return null;
            return new BindingOperator() { Left = op, Right = op2, Node = binding };
        }

        private Operator ParseInlineFunction(Node node, ErrorReport report, Using use)
        {
            if ( node[0].Id == (int)SafeScriptConstants.LOGICAL_OR )
                return this.ParseLogicalOr(node[0], report, use);
            if (node[0].Id == (int)SafeScriptConstants.SHORT_FUNCTION_DEF)
                return this.ParseShortFunctionDef(node[0], report, use);
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Operator ParseShortFunctionDef(Node node, ErrorReport report, Using use)
        {
            InlineFunctionOperator func = new InlineFunctionOperator() { Node = node };
            func.IsStatic = this.parsingStaticCode;

            Node shortParamList = node[0];
            Dictionary<string, object> scope = new Dictionary<string, object>();
            while (shortParamList != null)
            {
                string name = (shortParamList[0] as Token).Image;
                Parameter p = new Parameter() { Name = name };
                func.Parameters.Add(p);
                scope[name] = p;
                shortParamList = shortParamList[1];
            }
            use.Stack.Push(scope);
            func.Operator = this.ParseLogicalOr(node[2], report, use);
            // Some error?
            if (func.Operator == null)
                return null;
            //func.Statements = new StatementList();
            //func.Statements.Add( new ExpressionStatement() { Operator = this.ParseLogical(node[2], report, use) } );
            use.Stack.Pop();
            return func;
        }

        private Operator ParseLogicalOr(Node logical, ErrorReport report, Using use)
        {
            Operator op = this.ParseLogicalAnd(logical[0], report, use);
            if (op == null)
                return null;
            if (logical.Count == 1)
                return op;
            Node rest = logical[1];
            Operator op2 = this.ParseLogicalOr(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = logical };
        }

        private Operator ParseLogicalAnd(Node logical, ErrorReport report, Using use)
        {
            Operator op = this.ParseBitOr(logical[0], report, use);
            if (op == null)
                return null;
            if (logical.Count == 1)
                return op;
            Node rest = logical[1];
            Operator op2 = this.ParseLogicalAnd(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = logical };
        }

        private Operator ParseBitOr(Node logical, ErrorReport report, Using use)
        {
            Operator op = this.ParseBitXOr(logical[0], report, use);
            if (op == null)
                return null;
            if (logical.Count == 1)
                return op;
            Node rest = logical[1];
            Operator op2 = this.ParseBitOr(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = logical };
        }

        private Operator ParseBitXOr(Node logical, ErrorReport report, Using use)
        {
            Operator op = this.ParseBitAnd(logical[0], report, use);
            if (op == null)
                return null;
            if (logical.Count == 1)
                return op;
            Node rest = logical[1];
            Operator op2 = this.ParseBitXOr(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = logical };
        }

        private Operator ParseBitAnd(Node logical, ErrorReport report, Using use)
        {
            Operator op = this.ParseEquality(logical[0], report, use);
            if (op == null)
                return null;
            if (logical.Count == 1)
                return op;
            Node rest = logical[1];
            Operator op2 = this.ParseBitAnd(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = logical };
        }

        private Operator ParseEquality(Node cmp, ErrorReport report, Using use)
        {
            Operator op = this.ParseComparison(cmp[0], report, use);
            if (op == null)
                return null;
            if (cmp.Count == 1)
                return op;
            Node rest = cmp[1];
            Operator op2 = this.ParseEquality(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = cmp };
        }

        private Operator ParseComparison(Node cmp, ErrorReport report, Using use)
        {
            Operator op = this.ParseCastExpression(cmp[0], report, use);
            if (op == null)
                return null;
            if (cmp.Count == 1)
                return op;
            Node rest = cmp[1];
            Operator op2 = this.ParseComparison(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = cmp };
        }

        private Operator ParseCastExpression(Node cast, ErrorReport report, Using use)
        {
            Operator op = this.ParseShift(cast[0], report, use);
            if (cast.Count == 1)
                return op;
            Node rest = cast[1];
            if (rest[0].Id == (int)SafeScriptConstants.IS)
            {
                return new IsOperator() { Node = cast, Operator = op, Type = this.ParseTypeDef(rest[1], use, report) };
            }
            if (rest[0].Id == (int)SafeScriptConstants.AS)
            {
                return new AsOperator() { Node = cast, Operator = op, Type = this.ParseTypeDef(rest[1], use, report) };
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Operator ParseShift(Node cmp, ErrorReport report, Using use)
        {
            Operator op = this.ParseAddSub(cmp[0], report, use);
            if (op == null)
                return null;
            if (cmp.Count == 1)
                return op;
            Node rest = cmp[1];
            Operator op2 = this.ParseShift(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = cmp };
        }

        private Operator ParseAddSub(Node cmp, ErrorReport report, Using use)
        {
            Operator op = this.ParseMulDiv(cmp[0], report, use);
            if (op == null)
                return null;
            if (cmp.Count == 1)
                return op;
            Node rest = cmp[1];
            Operator op2 = this.ParseAddSub(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = cmp };
        }

        private Operator ParseMulDiv(Node cmp, ErrorReport report, Using use)
        {
            Operator op = this.ParseUnaryOperator(cmp[0], report, use);
            if (op == null)
                return null;
            if (cmp.Count == 1)
                return op;
            Node rest = cmp[1];
            Operator op2 = this.ParseMulDiv(rest[1], report, use);
            if (op2 == null)
                return null;
            return new BinaryOperator() { Left = op, Right = op2, Kind = (BinaryOperatorKind)(rest[0].Id), Node = cmp };
        }

        private Operator ParseUnaryOperator(Node node, ErrorReport report, Using use)
        {
            switch ((SafeScriptConstants)(node[0].Id))
            {
                case SafeScriptConstants.BANG:
                    {
                        Operator op = this.ParseUnaryOperator(node[1], report, use);
                        if (op == null)
                            return null;
                        return new NotOperator() { Operator = op, Node = node[0] };
                    }
                case SafeScriptConstants.ADD:
                    return new PlusOperator() { Operator = this.ParseUnaryOperator(node[1], report, use) };
                case SafeScriptConstants.SUB:
                    return new MinusOperator() { Operator = this.ParseUnaryOperator(node[1], report, use) };
                case SafeScriptConstants.INVERT:
                    return new InvertOperator() { Operator = this.ParseUnaryOperator(node[1], report, use) };
                case SafeScriptConstants.ARITHAND:
                    {
                        Operator op = this.ParseUnaryOperator(node[1], report, use);
                        this.parsingExpressionIsBinding = true;
                        Operator check = op;
                        if (check is IncDecOperator)
                            check = (check as IncDecOperator).Operator;
                        if (!(check is PropertyOperator))
                        {
                            report.Add(new ErrorEntry(node[1]) { Type = ErrorType.BindingNeedsPropertyOrFunction });
                            return null;
                        }
                        if (op is IncDecOperator)
                        {
                            IncDecOperator dop = op as IncDecOperator;
                            BindingTermOperator bop = new BindingTermOperator() { Node = node, Operator = dop.Operator as PropertyOperator };
                            dop.Operator = bop;
                            return dop;
                        }
                        return new BindingTermOperator() { Node = node, Operator = op as PropertyOperator };
                    }
                case SafeScriptConstants.MUL:
                    // TODO
                    System.Diagnostics.Debug.Assert(false, "Not implemented yet");
                    return null;
                case SafeScriptConstants.INC_DEC_TERM:
                    return this.ParseIncDecTerm(node[0], report, use);
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Operator ParseIncDecTerm(Node term, ErrorReport report, Using use)
        {
            if (term.Count == 1)
                return this.ParsePropertyTerm(term[0], report, use);
            IncDecOperatorKind kind = IncDecOperatorKind.PreDec;
            Operator op = null;
            if (term[0].Id == (int)SafeScriptConstants.INC_DEC_TERM_OP)
            {
                if (term[0][0].Id == (int)SafeScriptConstants.INCREMENT)
                    kind = IncDecOperatorKind.PreInc;
                else
                    kind = IncDecOperatorKind.PreDec;
                op = this.ParsePropertyTerm(term[1], report, use);
            }
            else
            {
                if (term[1][0].Id == (int)SafeScriptConstants.INCREMENT)
                    kind = IncDecOperatorKind.PostInc;
                else
                    kind = IncDecOperatorKind.PostDec;
                op = this.ParsePropertyTerm(term[0], report, use);
            }
            if (op == null)
                return null;
            return new IncDecOperator() { Node = term, Kind = kind, Operator = op };
        }

        private Operator ParsePropertyTerm(Node node, ErrorReport report, Using use)
        {
            Operator op = this.ParseFactor(node[0], report, use);
            if (op == null)
                return null;
            if (node.Count == 1)
                return op;
            if (op == null)
                return null;
            Node rest = node[1];
            return ParsePropertyTermRest(rest, report, use, op);
        }

        private PropertyTailOperator ParsePropertyTermRest(Node rest, ErrorReport report, Using use, Operator op)
        {
            if (rest[0].Id == (int)SafeScriptConstants.DOT)
            {
                // TODO: Handle the case that the Identifier has a TemplateArgs node as in 'Collections.List<int>.maxItems'.
                string name = (rest[1] as Token).Image;
                PropertyOperator result = new PropertyOperator() { Node = rest, Identifier = name, Operator = op };
                if (rest.Count == 3)
                {
                    PropertyTailOperator op2 = this.ParsePropertyTermRest(rest[2], report, use, result) as PropertyTailOperator;
                    return op2;
                }
                return result;
            }
            else if (rest[0].Id == (int)SafeScriptConstants.FUNCTION_CALL)
            {
                Node call = rest[0];
                Node paramList = Helper.GetNode(call, SafeScriptConstants.LIST_CONTENT);
                List<Operator> parameters = new List<Operator>();
                while (paramList != null)
                {
                    Operator po = this.ParseInlineFunction(paramList[0], report, use);
                    if (po == null)
                        return null;
                    parameters.Add(po);
                    paramList = Helper.GetNode(paramList, SafeScriptConstants.LIST_CONTENT);
                }
                FunctionCallOperator result = new FunctionCallOperator() { Node = rest, Operator = op, Parameters = parameters };
                if (rest.Count == 2)
                {
                    PropertyTailOperator op2 = this.ParsePropertyTermRest(rest[1], report, use, result) as PropertyTailOperator;
                    return op2;
                }
                return result;
            }
            else if (rest[0].Id == (int)SafeScriptConstants.INDEX)
            {
                Node indexNode = rest[0];
                IndexOperator ind = new IndexOperator() { Node = indexNode, Operator = op };
                Operator index = this.ParseLogicalOr(Helper.GetNode(indexNode, SafeScriptConstants.LOGICAL_OR), report, use);
                if (index == null)
                    return null;
                ind.Index = index;
                if (rest.Count == 2)
                {
                    PropertyTailOperator op3 = this.ParsePropertyTermRest(rest[1], report, use, ind) as PropertyTailOperator;
                    return op3;
                }
                return ind;
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Operator ParseFactor(Node node, ErrorReport report, Using use)
        {
            switch( (SafeScriptConstants)(node[0].Id) )
            {
                case SafeScriptConstants.ATOM:
                    return this.ParseAtom(node[0], report, use);
                case SafeScriptConstants.NEW:
                    return this.ParseNew(node, report, use);
                case SafeScriptConstants.LEFT_PAREN:
                    return this.ParseAssignment(node[1], report, use, false);
                case SafeScriptConstants.FUNCTION_DEF:
                    return this.ParseFunctionDef(node[0], report, use);
                case SafeScriptConstants.MAP:
                    return new MapOperator()
                    {
                        List = this.ParseLogicalOr(node[2], report, use),
                        Function = this.ParseInlineFunction(node[4], report, use)
                    };
                case SafeScriptConstants.REDUCE:
                    return new ReduceOperator()
                    {
                        List = this.ParseLogicalOr(node[2], report, use),
                        Function = this.ParseInlineFunction(node[4], report, use),
                        Initial = this.ParseLogicalOr(node[6], report, use)
                    };
                case SafeScriptConstants.FILTER:
                    return new FilterOperator()
                    {
                        List = this.ParseLogicalOr(node[2], report, use),
                        Function = this.ParseInlineFunction(node[4], report, use)
                    };
                case SafeScriptConstants.LEFT_SET:
                    return ParseDictContent(Helper.GetNode(node, SafeScriptConstants.DICT_CONTENT), report, use);
                case SafeScriptConstants.LEFT_LIST:
                    {
                        Node content = Helper.GetNode(node, SafeScriptConstants.LIST_CONTENT);
                        if (content == null)
                            return new ListContentOperator() { Node = node };
                        return ParseListContent(content, report, use);
                    }
                case SafeScriptConstants.XML:
                    return ParseXml(node[0], report, use);
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Xml.XmlTag ParseXml(Node node, ErrorReport report, Using use)
        {
            Node typedef = Helper.GetNode(node, SafeScriptConstants.SIMPLE_TYPE_DEF);
            AbstractType t = this.ParseSimpleTypeDef(use, report, typedef);
            if (t == null)
                return null;

            Node xmlrest = Helper.GetNode(node, SafeScriptConstants.XML_REST);
            Node mixed = Helper.GetNode(xmlrest, SafeScriptConstants.MIXED_XML);
            Node attribs = Helper.GetNode(node, SafeScriptConstants.XML_ATTRIBUTES);

            Xml.XmlTag tag = new SafeScript.MetaModel.Xml.XmlTag() { Node = node, Type = t, IsStatic = this.parsingStaticCode };

            //
            // Parse the attributes
            //
            while (attribs != null)
            {
                // Get the name of the attribute
                string aname = Helper.GetToken(attribs, SafeScriptConstants.IDENTIFIER).Image;
                if (tag.Attributes.ContainsKey(aname))
                {
                    report.Add(new ErrorEntry(attribs) { Info = aname, Type = ErrorType.DuplicateName });
                    return null;
                }

                // What does the name mean for type t?
                object o = t.GetFlatScopeElement(aname);
                if ( o == null )
                {
                    report.Add( new ErrorEntry(attribs) { Info = aname, Type = ErrorType.UnknownIdentifier });
                    return null;
                }

                // Do we set a member variable or property?
                if (o is MemberVariable)
                {
                    MemberVariable obj = o as MemberVariable;
                    if (obj.IsStatic)
                    {
                        report.Add(new ErrorEntry(attribs) { Info = aname, Type = ErrorType.NoStaticMemberInXml });
                        return null;
                    }
                    // If it is a property, it must feature a setter (except for the style property)
                    Property prop = obj as Property;
                    if (prop != null && prop.Setter == null && tag.IsHTML && aname != "style")
                        report.Add(new ErrorEntry(attribs) { Type = ErrorType.PropertyHasNoSetter, Info = prop.Name });

                    Xml.XmlAttribute attrib = new SafeScript.MetaModel.Xml.XmlAttribute() { Name = aname, Node = attribs, Var = obj };
                    tag.Attributes[aname] = attrib;

                    if (Helper.GetToken(attribs, SafeScriptConstants.STRING) != null)
                    {
                        string str = Helper.GetToken(attribs, SafeScriptConstants.STRING).Image;
                        // TODO: propertly escape string
                        attrib.StringValue = str.Substring(1, str.Length - 2);
                    }
                    else
                    {
                        Operator op = this.ParseBinding(Helper.GetNode(attribs, SafeScriptConstants.BINDING), report, use, true);
                        if (op == null)
                            return null;
                        attrib.OperatorValue = op;
                    }
                }
                // Do we connect to a DOM event?
                else if (o is MemberEvent)
                {
                    MemberEvent obj = o as MemberEvent;
                    if (obj.IsStatic)
                    {
                        report.Add(new ErrorEntry(attribs) { Info = aname, Type = ErrorType.NoStaticMemberInXml });
                        return null;
                    }

                    Xml.XmlAttribute attrib = new SafeScript.MetaModel.Xml.XmlAttribute() { Name = aname, Node = attribs, Event = obj };
                    tag.Attributes[aname] = attrib;

                    if (Helper.GetToken(attribs, SafeScriptConstants.STRING) != null)
                    {
                        report.Add(new ErrorEntry(attribs) { Info = aname, Type = ErrorType.IncompatibleTypes });
                        return null;
                    }
                    Operator op = this.ParseBinding(Helper.GetNode(attribs, SafeScriptConstants.BINDING), report, use, true);
                    if (op == null)
                        return null;
                    attrib.OperatorValue = op;
                }

                attribs = Helper.GetNode(attribs, SafeScriptConstants.XML_ATTRIBUTES);
            }

            int startLine = -1;
            int startCol = -1;
            //if (mixed != null)
            //{
            //    startLine = mixed.StartLine;
            //    startCol = mixed.StartColumn;
            //}
            while (mixed != null)
            {
                Node xml = Helper.GetNode(mixed, SafeScriptConstants.XML);
                // New XML Tag?
                if (xml != null)
                {
                    int endLine = xml.StartLine;
                    int endColumn = xml.StartColumn;
                    if (startLine != -1 && (endLine != startLine || endColumn != startCol))
                    {
                        tag.Content.Add(new Xml.XmlText() { Value = Compiler.CurrentCompiler.GetText( Compiler.CurrentCompiler.GetFilename(xml), startLine, startCol, endLine, endColumn) });
                        startLine = -1;
                    }
                    Xml.XmlTag t2 = this.ParseXml(xml, report, use);
                    // Some error?
                    if (t2 == null)
                        return null;
                    tag.Content.Add(t2);
                }
                // End tag?
                else if (mixed.Count == 4 && mixed[0].Id == (int)SafeScriptConstants.LESS)
                {
                    int endLine = mixed.StartLine;
                    int endColumn = mixed.StartColumn;
                    if (startLine != -1 && (endLine != startLine || endColumn != startCol))
                    {
                        tag.Content.Add(new Xml.XmlText() { Value = Compiler.CurrentCompiler.GetText(Compiler.CurrentCompiler.GetFilename(mixed), startLine, startCol, endLine, endColumn) });
                        startLine = -1;
                    }

                    Node typedef2 = Helper.GetNode(mixed, SafeScriptConstants.SIMPLE_TYPE_DEF);
                    AbstractType t2 = this.ParseSimpleTypeDef(use, report, typedef2);
                    if (t2 == null)
                        return null;
                    if (t != t2)
                    {
                        report.Add(new ErrorEntry(mixed) { Type = ErrorType.WrongClosingTag });
                        return null;
                    }
                    break;
                }
                // Expression?
                else if (mixed.Count == 4 && mixed[0].Id == (int)SafeScriptConstants.LEFT_SET)
                {
                    int endLine = mixed.StartLine;
                    int endColumn = mixed.StartColumn;
                    if (startLine != -1 && (endLine != startLine || endColumn != startCol))
                    {
                        tag.Content.Add(new Xml.XmlText() { Value = Compiler.CurrentCompiler.GetText(Compiler.CurrentCompiler.GetFilename(mixed), startLine, startCol, endLine, endColumn) });
                        startLine = -1;
                    }

                    Node logical = Helper.GetNode(mixed, SafeScriptConstants.BINDING);
                    Operator op = this.ParseBinding(logical, report, use, true);
                    if (op == null)
                        return null;
                    tag.Content.Add(op);
                }
                // Some text character, symbole, what so ever
                else if ( startLine == -1 )
                {
                    startLine = mixed.StartLine;
                    startCol = mixed.StartColumn;
                }

                // Continue
                mixed = Helper.GetNode(mixed, SafeScriptConstants.MIXED_XML);
            }

            return tag;
        }

        private Operator ParseListContent(Node node, ErrorReport report, Using use)
        {
            ListContentOperator op = new ListContentOperator() { Node = node };

            while (node != null)
            {
                op.Values.Add(this.ParseInlineFunction(node[0], report, use));
                node = Helper.GetNode(node, SafeScriptConstants.LIST_CONTENT);
            }

            return op;
        }

        private Operator ParseDictContent(Node node, ErrorReport report, Using use)
        {
            DictContentOperator op = new DictContentOperator() { Node = node };

            while (node != null)
            {
                string name = null;
                if ((SafeScriptConstants)node[0].Id == SafeScriptConstants.IDENTIFIER)
                    name = (node[0] as Token).Image;
                else if ((SafeScriptConstants)node[0].Id == SafeScriptConstants.STRING)
                {
                    // TODO: Unescape the string
                    var str = (node[0] as Token).Image;
                    name = str.Substring(1, str.Length - 2);
                }
                else
                    System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
                op.Values[name] = this.ParseInlineFunction(node[2], report, use);
                node = Helper.GetNode(node, SafeScriptConstants.DICT_CONTENT);
            }

            return op;
        }

        private Operator ParseFunctionDef(Node node, ErrorReport report, Using use)
        {
            Node parameterList = Helper.GetNode(node, SafeScriptConstants.PARAMETER_LIST);
            Node returnTypeDef = Helper.GetNode(node, SafeScriptConstants.TYPE_DEF);
            Node statements = Helper.GetNode(node, SafeScriptConstants.STATEMENT_LIST);

            FunctionOperator func = new FunctionOperator() { Node = node };
            func.IsStatic = this.parsingStaticCode;

            // Return type other than void?
            if (returnTypeDef != null)
            {
                ITypeDef rt = this.ParseTypeDef(returnTypeDef, use, report);
                if (rt == null)
                    return null;
                func.ReturnType = new InstanceType() { Type = rt };
            }
            else
                func.ReturnType = VoidType.Self;

            // Function parameters?
            Dictionary<string, object> scope = new Dictionary<string, object>();
            if (parameterList != null)
            {
                while (parameterList != null)
                {
                    Node paramTypeDef = Helper.GetNode(parameterList, SafeScriptConstants.TYPE_DEF);
                    ITypeDef paramType = this.ParseTypeDef(paramTypeDef, use, report);
                    if (paramType == null)
                        return null;
                    string paramName = Helper.GetToken(parameterList, SafeScriptConstants.IDENTIFIER).Image;
                    Parameter p = new Parameter() { Name = paramName, Type = new InstanceType() { Type = paramType } };
                    if (scope.ContainsKey(paramName))
                        this.ReportDuplicateName(parameterList, report, paramName);
                    func.Parameters.Add(p);
                    scope[paramName] = p;
                    parameterList = Helper.GetNode(parameterList, SafeScriptConstants.PARAMETER_LIST);
                }
            }

            if (statements != null)
            {
                use.Stack.Push(scope);
                object old = this.parsingContext;
                bool oldret = this.parsingContextHasReturns;
                this.parsingContextHasReturns = false;
                this.parsingContext = func;
                func.Statements = this.ParseStatementList(statements, report, use);
                this.parsingContext = old;
                this.parsingContextHasReturns = oldret;
                use.Stack.Pop();
            }

            return func;
        }

        private Operator ParseNew(Node node, ErrorReport report, Using use)
        {
            ITypeDef type = this.ParseTypeDef(node[1], use, report);
            NewOperator op = new NewOperator() { Type = type, Node = node };

            Node newinit = Helper.GetNode(node, SafeScriptConstants.NEW_INIT);
            if (newinit != null)
            {
                Operator op2 = this.ParseNewInit(newinit, report, use);
                if ( op2 is FunctionCallOperator )
                {
                    FunctionCallOperator call = op2 as FunctionCallOperator;
                    call.Operator = op;
                    return call;
                }
                op.Initial = op2;
            }

            return op;
        }

        private Operator ParseNewInit(Node newinit, ErrorReport report, Using use)
        {
            switch ((SafeScriptConstants)newinit[0].Id)
            {
                case SafeScriptConstants.LEFT_SET:
                    // TODO
                    return null;
                case SafeScriptConstants.LEFT_LIST:
                    // TODO
                    return null;
                case SafeScriptConstants.FUNCTION_CALL:
                    return this.ParseFunctionCall(newinit[0], report, use);
            }
            System.Diagnostics.Debug.Assert(false, "Unknown grammar rule");
            return null;
        }

        private Operator ParseFunctionCall(Node call, ErrorReport report, Using use)
        {
            Node paramList = Helper.GetNode(call, SafeScriptConstants.LIST_CONTENT);
            List<Operator> parameters = new List<Operator>();
            while (paramList != null)
            {
                Operator op = this.ParseInlineFunction(paramList[0], report, use);
                if (op == null)
                    return null;
                parameters.Add(op);
                paramList = Helper.GetNode(paramList, SafeScriptConstants.LIST_CONTENT);
            }
            FunctionCallOperator result = new FunctionCallOperator() { Node = call, Parameters = parameters };
            return result;         
        }

        private Operator ParseAtom(Node node, ErrorReport report, Using use)
        {
            switch ((SafeScriptConstants)node[0].Id)
            {
                case SafeScriptConstants.IDENTIFIER:
                    {
                        string name = (node[0] as Token).Image;
                        // Try to resolve the name
                        object obj = ScopeResolver.FindObject(this, name, use, true);
                        if (obj == null)
                        {
                            if (name == "value" && this.parsingPropertySetType != null)
                                return new Atom() { Node = node, Kind = AtomKind.VALUE, Type = this.parsingPropertySetType };
                            report.Add(new ErrorEntry(node) { Type = ErrorType.UnknownIdentifier, Info = name });
                            return null;
                        }
                        if (this.parsingStaticCode)
                        {
                            if ((obj is MemberVariable && (obj as MemberVariable).IsStatic == false) || (obj is MemberFunction && (obj as MemberFunction).IsStatic == false ))
                            {
                                report.Add(new ErrorEntry(node) { Type = ErrorType.NonStaticMemberNotAccessible, Info = name });
                                return null;
                            }
                        }
                        return new AtomIdentifier() { Node = node, Kind = (AtomKind)node[0].Id, Identifier = name, Object = obj };
                    }
                case SafeScriptConstants.STRING:
                    {
                        string str = (node[0] as Token).Image;
                        // Strip the quotes
                        str = str.Substring(1, str.Length - 2);
                        // TODO: Unescape the string
                        return new AtomStringValue() { Node = node, Kind = (AtomKind)node[0].Id, Value = str, Type = new InstanceType() { Type = StringType.Self } };
                    }
                case SafeScriptConstants.TRUE:
                    return new AtomBoolValue() { Node = node, Kind = (AtomKind)node[0].Id, Value = true, Type = new InstanceType() { Type = BoolType.Self } };
                case SafeScriptConstants.FALSE:
                    return new AtomBoolValue() { Node = node, Kind = (AtomKind)node[0].Id, Value = false, Type = new InstanceType() { Type = BoolType.Self } };
                case SafeScriptConstants.NULL:
                    return new AtomNull() { Node = node, Kind = (AtomKind)node[0].Id, Type = new InstanceType() { Type = NullType.Self } };
                case SafeScriptConstants.THIS:
                    {
                        Class cl = this as Class;
                        if (cl == null)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.ThisNotAllowed });
                            return null;
                        }
                        return new Atom() { Node = node, Kind = AtomKind.THIS, Type = new InstanceType() { Type = cl } };
                    }
                case SafeScriptConstants.INTVALUE:
                    {
                        string i = (node[0] as Token).Image;
                        try
                        {
                            return new AtomNumValue() { Node = node, Kind = (AtomKind)node[0].Id, Value = int.Parse(i), Type = new InstanceType() { Type = NumericType.Self } };
                        }
                        catch (FormatException)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.NotAValidInteger, Info = i });
                            return null;
                        }
                        catch (OverflowException)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.NotAValidInteger, Info = i });
                            return null;
                        }
                    }
                case SafeScriptConstants.FLOATVALUE:
                    {
                        string i = (node[0] as Token).Image;
                        try
                        {
                            return new AtomNumValue() { Node = node, Kind = (AtomKind)node[0].Id, Value = double.Parse(i), Type = new InstanceType() { Type = NumericType.Self } };
                        }
                        catch (FormatException)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.NotAValidFloat, Info = i });
                            return null;
                        }
                        catch (OverflowException)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.NotAValidFloat, Info = i });
                            return null;
                        }
                    }
                case SafeScriptConstants.SUPER:
                    {
                        Class cl = this as Class;
                        if (cl == null || cl.BaseClass == null)
                        {
                            report.Add(new ErrorEntry(node) { Type = ErrorType.SuperNotAllowed });
                            return null;
                        }
                        return new Atom() { Node = node, Kind = AtomKind.SUPER, Type = new InstanceType() { Type = cl.BaseClass } };
                    }
            }

            return new Atom() { Kind = (AtomKind)node[0].Id };
        }

        public void ComputeMemberVariableTypes(ErrorReport report)
        {
            // Recursion
            foreach (string name in this.ScopeElements.Keys.ToArray())
            {
                object obj = this.ScopeElements[name];
                Namespace nspace = obj as Namespace;
                if (nspace != null)
                {
                    nspace.ComputeMemberVariableTypes(report);
                    continue;
                }
                MemberVariable memberVar = obj as MemberVariable;
                if (memberVar != null)
                    memberVar.ComputeType(report);
            }
        }

        private AbstractType FindAbstractType(ScopedIdentifier ident, Using use)
        {
            Namespace nspace = this.FindNamespace(ident[0], use, true);
            if (nspace == null)
                return null;
            if (ident.Count == 1)
                return nspace as AbstractType;
            ScopeResolution res = nspace.GetScopeElement(ident, 1);
            if ( res == null )
                return null;
            return res.GetFinalElement() as AbstractType;
        }

        private Namespace FindNamespace(string ident, Using use, bool checkParentScope)
        {
            Scope scope = this;
            while (scope != null)
            {
                if (use != null && use.Stack.Count > 0)
                {
                    for (int i = 0; i < use.Stack.Count; ++i)
                    {
                        Dictionary<string, object> dict = use.Stack.ElementAt(i);
                        object obj2 = null;
                        if (dict.TryGetValue(ident, out obj2) && obj2 is Namespace)
                            return obj2 as Namespace;
                    }
                }

                object obj = scope.GetScopeElement(ident);
                if (obj is Namespace)
                    return obj as Namespace;

                if (scope is AbstractType)
                {
                    AbstractType at = scope as AbstractType;

                    // Check the template parameters
                    foreach (AbstractType tt in at.TemplateParameters)
                    {
                        if (tt.Name == ident)
                            return tt;
                    }

                    // If the context is a type (class/interface), we must check the base types, too.
                    foreach (AbstractType bt in at.BaseTypes)
                    {
                        Namespace result = bt.FindNamespace(ident, null, false);
                        if (result != null)
                            return result;
                    }
                }

                // Nothing found in the initial context? -> look at the imported and alias namespaces
                if (use != null)
                {
                    // Try the alias namespaces
                    Namespace alias = null;
                    if (use.Alias.TryGetValue(ident, out alias))
                        return alias;
                    // Try imported namespaces
                    foreach (Namespace u in use.Imported)
                    {
                        object res = u.GetScopeElement(ident);
                        if (res != null && res is Namespace)
                            return res as Namespace;
                    }
                }
                if (!checkParentScope)
                    return null;
                scope = scope.ParentScope;
                if (use != null)
                    use = use.ParentUsing;
            }
            return null;
        }    

        private void ReportDuplicateName(Node location, ErrorReport report, string name)
        {
            report.Add(new ErrorEntry(location)
            {
                Type = ErrorType.DuplicateName,
                Info = name
            });
        }

        public T GetAttribute<T>() where T : MemberAttribute
        {
            if (this.Attributes == null)
                return null;
            foreach (MemberAttribute attr in this.Attributes)
            {
                if (attr is T)
                    return (T)attr;
            }
            return default(T);
        }

        private void RegisterVariable(VarStatement varStatement)
        {
            if (this.parsingContext is MemberFunction)
                (this.parsingContext as MemberFunction).Variables.Add(varStatement);
        }

        public Operator ParseConstant(Node inlineFunction, ErrorReport report)
        {
            Using use = new Using();
            return this.ParseInlineFunction(inlineFunction, report, use);
        }

    }
}
